﻿// Copyright (c) 2012 - 2021 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2021-03-19 version 4.0.0 Jared Zheng <jaredz at outlook dot com>

#ifndef __PACKET_STREAM_H__
#define __PACKET_STREAM_H__

#pragma once

#include "CommonDefine.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 信令包流模板 : TPAK信令包数据 + 动态数据
template <typename TPAK>
class CTPAKStreamBase : public TPAK
{
public:
    CTPAKStreamBase(void);
    virtual ~CTPAKStreamBase(void) OVERRIDE;

    virtual size_t Length(void) const OVERRIDE;
    virtual void   Serialize(CStream&) OVERRIDE;
    virtual bool   IsStream(void) const OVERRIDE;
    virtual bool   Bind(CNetworkPtr& NetworkPtr, CNetParam* pNetParam) OVERRIDE;

    // 信令包流是否有有效缓存
    bool   IsValid(void) const;
    // 信令包流是否是jumbo缓存
    bool   IsJumbo(void) const;
    // 地址数据是否有效
    bool   AddrValid(void) const;

    CNetAddr& GetAddr(void);
    const CNetAddr& GetAddr(void) const;
    void      SetAddr(CNetAddr& NetAddr);

    // 返回TPAK's Length大小, 注意动态数据都应该追加到TPAK数据的后面
    size_t PakSize(void) const;
    // bIncPak = false, 返回排除TPAK's Length的缓存大小
    // bIncPak = true,  返回包含TPAK's Length的缓存大小
    size_t BufSize(bool bIncPak = false) const;

    // 检测临时读数据流是否有效
    bool     IsTempStream(void) const;
    // 获取临时读数据流
    // sPos=0表示Tell()保持原有的不变
    // sPos>0表示Tell()设置为sPos
    // sPos<0表示Tell()设置为TPAK's Length大小
    CStream& GetTempStream(SeekPos sPos = (SeekPos)0);

    // Reset时候会把TPAK数据重新写入要发送缓存的开始位置
    // sPos=0表示Tell()保持原有的不变
    // sPos>0表示Tell()设置为sPos
    // sPos<0表示Tell()设置为TPAK's Length大小
    bool ResetPos(SeekPos sPos = (SeekPos)-1);
    // 获取信令包流
    // sPos=0表示Tell()保持原有的不变
    // sPos>0表示Tell()设置为sPos
    // sPos=-1表示Tell()设置为TPAK's Length大小
    // sPos=-2设置为0
    CBufStreamBase& GetStream(bool bRead = true, SeekPos sPos = (SeekPos)-1);

    // 信令包流转换成网络包流(bReset=true调用Reset(0)重新写入TPAK数据), NetworkPtr与创建实例传入的必须一致
    bool GetBuffer(CNetworkPtr& NetworkPtr, CNetStreamPtr& StreamPtr, bool bReset = true);

    bool Attach(CNetworkPtr& NetworkPtr, CNetParam* pNetParam);
    void Detach(void);

    bool Alloc(CNetworkPtr& NetworkPtr, bool bJumbo = false);
    void Free(void);
private:
    CTPAKStreamBase(const CTPAKStreamBase& aSrc);
    CTPAKStreamBase& operator=(const CTPAKStreamBase& aSrc);

    bool GetRead(SeekPos sPos);
    bool GetWrite(SeekPos sPos, bool bReset = true);

    void SetAddrParam(CNetParam* pNetParam);
private:
    // 临时读数据流, 从只读流源实例读取完TPAK数据以后, 会保留只读流源实例, 这样可以通过这个临时流指针读取后续的数据
    CStream*          m_pTempStream;
    CAddrParam        m_AddrParam; // 网络数据包参数, stSize为TPAK信令包数据大小
    // 读写信令包流都使用同一个缓存
    // 如果写信令包流未创建, 读信令包流是原来网络包数据(Attach), 或者空数据(Alloc)
    // 如果写信令包流创建, 读信令包流为写信令包流的引用
    CBufReadStream    m_BufRead;   // 读信令包流
    CBufWriteStream   m_BufWrite;  // 写信令包流
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "PacketStream.inl"

#endif // __PACKET_STREAM_H__
